﻿//---------------------------------------------------------------------
// Copyright (c) 2007 Digerati Technologies, LLC.  All Rights Reserved.
//
// Release under the Microsoft Public License (Ms-PL).
//---------------------------------------------------------------------

using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;

namespace DigeratiTech.NDiagnostics
{
    /// <summary>
    /// This class provides the main UI for the NDiagnostics application.
    /// </summary>
    public partial class Main : Form
    {
        #region Constants...

        private const int IMG_CHECKING = 1;
        private const int IMG_COMPLETE = 2;
        private const int IMG_ERROR = 3;
        private const int IMG_START = 4;
        private const int IMG_STOP = 5;
        private const int IMG_WARNING = 6;

        #endregion

        #region Private Attributes...

        private string _configurationFile;
        private TaskHandler _taskHandler;

        #endregion

        #region Constructors...

        /// <summary>
        /// Initializes a new instance of the <see cref="Main"/> class.
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        public Main(string configurationFile)
        {
            InitializeComponent();

            Application.EnableVisualStyles();

            _configurationFile = configurationFile;

            // Create a new task handler
            _taskHandler = new TaskHandler();

            // Setup ListView columns
            lvGroups.Columns.Add("Task", 200, HorizontalAlignment.Left);
            lvGroups.Columns.Add("Status", 75, HorizontalAlignment.Left);
            lvGroups.Columns.Add("Message", 300, HorizontalAlignment.Left);
        }

        #endregion

        #region Event Handlers...

        /// <summary>
        /// Initializes the form.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Main_Load(object sender, EventArgs e)
        {
            // Load the diagnostics configuration file
            if (string.IsNullOrEmpty(_configurationFile))
            {
                // Check for default "NDiagnostics.xml" file
                if (File.Exists("NDiagnostics.xml"))
                {
                    _configurationFile = "NDiagnostics.xml";
                }
            }

            // If the default file is not found and no configuration filename was passed in, then use the built-in configuration resource file
            if (string.IsNullOrEmpty(_configurationFile))
            {
                // Load resource file
                _taskHandler.Load();
            }
            else
            {
                // Load specified configuration file
                _taskHandler.Load(_configurationFile);
            }

            // Display the title
            this.Text = string.Format("{0} - NDiagnostics", _taskHandler.Title);

            // Initialize the display
            InitializeDisplay();
        }

        /// <summary>
        /// Starts the scanning process if it is not already running; otherwise, cancels the current scan.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnStartScan_Click(object sender, EventArgs e)
        {
            if (!bgWorker.IsBusy)
                StartScan();
            else
                StopScan();
        }

        /// <summary>
        /// Exits the application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnExit_Click(object sender, EventArgs e)
        {
            // Stop an existing scan
            if (bgWorker.IsBusy)
                StopScan();

            this.Close();
        }

        /// <summary>
        /// This method performs the asynchronous scan via the BackgroundWorkerThread control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void gbWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int taskCount = 0;

            foreach (TaskGroup taskGroup in _taskHandler.TaskGroups)
            {
                foreach (DiagnosticTask task in taskGroup.Tasks)
                {
                    // Check for user cancellation
                    if (bgWorker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }

                    taskCount++;

                    task.Status = TaskStatus.Scanning;
                    bgWorker.ReportProgress((int)((decimal)taskCount / _taskHandler.TaskCount * 100m), task);
                    System.Threading.Thread.Sleep(0);

                    // Execute the current task
                    task.Execute();

                    bgWorker.ReportProgress((int)((decimal)taskCount / _taskHandler.TaskCount * 100m), task);
                    System.Threading.Thread.Sleep(0);
                }
            }
        }

        /// <summary>
        /// Display the progress of the current scan.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        private void gbWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.SuspendLayout();

            DiagnosticTask task = (DiagnosticTask)e.UserState;

            // Locate ListView node to update status
            ListViewItem lvItem = GetListViewItem(task); ;

            // Display the appropriate status
            lvItem.SubItems[1].Text = task.StatusText;
            lvItem.SubItems[2].Text = task.Message;
            lvItem.EnsureVisible();
            lvItem.BackColor = System.Drawing.SystemColors.Window;

            switch (task.Status)
            {
                case TaskStatus.Scanning:
                    lvItem.ImageIndex = IMG_CHECKING;
                    break;

                case TaskStatus.Completed:
                    lvItem.ImageIndex = IMG_COMPLETE;
                    break;

                case TaskStatus.Error:
                    lvItem.ImageIndex = IMG_ERROR;
                    lvItem.BackColor = System.Drawing.Color.LightCoral;
                    break;

                case TaskStatus.Warning:
                    lvItem.ImageIndex = IMG_WARNING;
                    break;

                case TaskStatus.NotRun:
                    lvItem.ImageIndex = -1;
                    break;
            }

            // Update the progress bar
            pbProgress.Value = e.ProgressPercentage;

            this.ResumeLayout();
        }

        /// <summary>
        /// Resets the UI after the current scan has completed (or has been cancelled).
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void gbWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            btnStartScan.Text = "Start Scan";
            btnStartScan.ImageIndex = IMG_START;
            menuStartScan.Image = imgIcons.Images[IMG_START];
            menuStartScan.Text = btnStartScan.Text;

            // Update status counts
            lblPassed.Text = string.Format("{0} Passed", _taskHandler.GetStatusCount(TaskStatus.Completed));
            lblFailed.Text = string.Format("{0} Failed", _taskHandler.GetStatusCount(TaskStatus.Error));
            lblWarning.Text = string.Format("{0} Warnings", _taskHandler.GetStatusCount(TaskStatus.Warning));
            lblSkipped.Text = string.Format("{0} Skipped", _taskHandler.GetStatusCount(TaskStatus.NotRun));

            pbProgress.Visible = false;
            pnlStatus.Visible = true;

            lvGroups.BeginUpdate();

            // Display a status of cancelled for any incomplete tasks
            foreach (ListViewItem item in lvGroups.Items)
            {
                if ((item.ImageIndex == IMG_CHECKING) || (item.ImageIndex < 0))
                {
                    item.SubItems[1].Text = "Cancelled";
                }
            }

            lvGroups.EndUpdate();
        }

        #endregion

        #region Methods...

        /// <summary>
        /// Starts the scan.
        /// </summary>
        private void StartScan()
        {
            btnStartScan.Text = "Cancel";
            btnStartScan.ImageIndex = IMG_STOP;
            menuStartScan.Image = imgIcons.Images[IMG_STOP];
            menuStartScan.Text = btnStartScan.Text;

            // Initialize the display
            InitializeDisplay();

            // Display the progress bar
            pbProgress.Visible = true;

            // Run tasks
            bgWorker.RunWorkerAsync();
        }

        /// <summary>
        /// Stops the current scan.
        /// </summary>
        private void StopScan()
        {
            btnStartScan.Text = "Start Scan";
            btnStartScan.ImageIndex = IMG_START;
            menuStartScan.Image = imgIcons.Images[IMG_START];
            menuStartScan.Text = btnStartScan.Text;

            pbProgress.Visible = false;

            bgWorker.CancelAsync();
        }

        /// <summary>
        /// Initializes the display with the current set of groups and tasks to be executed.
        /// </summary>
        private void InitializeDisplay()
        {
            try
            {
                ListViewGroup newGroup;

                lvGroups.BeginUpdate();
                lvGroups.Items.Clear();
                lvGroups.Groups.Clear();

                foreach (TaskGroup taskGroup in _taskHandler.TaskGroups)
                {
                    // Add the ListView group
                    newGroup = lvGroups.Groups.Add(taskGroup.Name, taskGroup.Name);

                    // Load each task for the current group
                    foreach (DiagnosticTask task in taskGroup.Tasks)
                    {
                        ListViewItem lvItem = new ListViewItem(new string[] { task.Description, task.StatusText, string.Empty }, newGroup);

                        // Set a key for later retrieval
                        lvItem.Tag = task.Id;

                        lvGroups.Items.Add(lvItem);
                    }
                }

                // Initialize the progress bar
                pbProgress.Value = 0;
                pbProgress.Visible = false;
                pnlStatus.Visible = false;
            }
            catch
            {
                // TODO: Handle errors
            }
            finally
            {
                lvGroups.EndUpdate();
            }
        }

        /// <summary>
        /// Search for a ListViewItem matching the specified <paramref name="task"/>.
        /// </summary>
        /// <param name="task">The task to match.</param>
        /// <returns>A <see cref="ListViewItem"/> matching the specified <paramref name="task"/> if it is found; otherwise, null.</returns>
        private ListViewItem GetListViewItem(DiagnosticTask task)
        {
            foreach (ListViewItem item in lvGroups.Items)
            {
                if (item.Tag.ToString().Equals(task.Id))
                    return item;
            }

            return null;
        }

        /// <summary>
        /// Displays the NDiagnostics About dialog.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void menuShowAboutDialog_Click(object sender, EventArgs e)
        {
            using (About aboutDlg = new About())
            {
                aboutDlg.ShowDialog();
            }
        }

        #endregion
    }
}
